package com.kun.config;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 按照次序选择服务实例，实现自定义负载均衡算法
 */
public class DiyLoadBalancerConfiguration implements ReactorServiceInstanceLoadBalancer {


    private static final Log log = LogFactory.getLog(RandomLoadBalancer.class); //日志记录

    private final String serviceId; //服务名称

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    /**
     * @param serviceInstanceListSupplierProvider 服务实例列表供应商
     * {@link ServiceInstanceListSupplier} 将用于获取可用实例的服务实例列表供应商
     * @param serviceId 待选择的服务名称
     */
    public DiyLoadBalancerConfiguration(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
                              String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @SuppressWarnings("rawtypes") //移除警告
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);  //获取服务实例列表供应商，如果没有提供，则使用NoopServiceInstanceListSupplier
        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); //获取服务实例列表，并处理响应
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances) {
        Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); //获取服务实例响应
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { //如果服务实例列表供应商是SelectedInstanceCallback，则调用selectedServiceInstance方法
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); //调用selectedServiceInstance方法
        }
        return serviceInstanceResponse;
    }

    private int totals = 0;  //计数器
    private int currentIndex = 0;  //当前索引

    //修改该方法即可，实现自定义负载均衡算法
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {  //如果服务实例列表为空，则返回空响应
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + serviceId);
            }
            return new EmptyResponse();
        }
        totals++;  //计数器加1
        currentIndex +=2;
        int index = currentIndex;  //随机获取服务实例索引
        if (index >= instances.size()) {  //如果索引大于等于服务实例列表大小，则重置索引
            currentIndex = 0;
            index = currentIndex++;  //随机获取服务实例索引
        }

        ServiceInstance instance = instances.get(index);  //获取服务实例

        return new DefaultResponse(instance);  //返回服务实例响应
    }
}
